﻿
using System;

namespace Okarito.Utility
{
    /// <summary>
    /// Executes the specified delegate when disposed.
    /// </summary>
    public sealed class Scope : IDisposable
    {
        /// <summary>
        /// Creates a <see cref="Scope" /> for the specified delegate.
        /// </summary>
        /// <param name="fnDispose">The delegate.</param>
        /// <returns>An instance of <see cref="Scope" /> that calls the delegate when disposed.</returns>
        /// <remarks><para>If fnDispose is null, the instance does nothing when disposed.</para>
        /// <para>From <a href="http://code.logos.com/blog/2008/08/leverage_using_blocks_with_scope.html">http://code.logos.com/blog/2008/08/leverage_using_blocks_with_scope.html</a>.</para></remarks>
        public static Scope Create(Action fnDispose)
        {
            return new Scope(fnDispose);
        }

        /// <summary>
        /// An empty <see cref="Scope"/>, which does nothing when disposed.
        /// </summary>
        public static readonly Scope Empty = new Scope(null);

        /// <summary>
        /// Cancel the call to the encapsulated delegate.
        /// </summary>
        /// <remarks>After calling this method, disposing this instance does nothing.</remarks>
        public void Cancel()
        {
            m_fnDispose = null;
        }

        /// <summary>
        /// Returns a new <see cref="Scope"/> that will call the encapsulated delegate.
        /// </summary>
        /// <returns>A new <see cref="Scope"/> that will call the encapsulated delegate.</returns>
        /// <remarks>After calling this method, disposing this instance does nothing.</remarks>
        public Scope Transfer()
        {
            Scope scope = new Scope(m_fnDispose);
            m_fnDispose = null;
            return scope;
        }

        /// <summary>
        /// Calls the encapsulated delegate.
        /// </summary>
        public void Dispose()
        {
            if (m_fnDispose != null)
            {
                m_fnDispose();
                m_fnDispose = null;
            }
        }

        private Scope(Action fnDispose)
        {
            m_fnDispose = fnDispose;
        }

        Action m_fnDispose;
    }
}
